#include <FLAC/all.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>

#include "flac.h"
#include "log.h"

FLAC__StreamDecoderWriteStatus
write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame,
               const FLAC__int32 *const buffer[], void *client_data) {
    uint32_t blocksize = frame->header.blocksize;
    uint8_t channels = frame->header.channels;
    uint64_t sample_number = frame->header.number.sample_number;
    FlacData *fd = (FlacData *)client_data;
    int16_t *wav_b = &fd->wav[channels * sample_number];
    for (int i = 0; i < blocksize; i++) {
        int16_t *wav_c = &wav_b[i * channels];
        for (int j = 0; j < channels; j++) {
            wav_c[j] = (FLAC__int16)buffer[j][i];
        }
    }

    return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}

void metadata_callback(const FLAC__StreamDecoder *decoder,
                       const FLAC__StreamMetadata *metadata,
                       void *client_data) {
    FlacData *fd = (FlacData *)client_data;
    if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
        /* save for later */
        fd->total_samples = metadata->data.stream_info.total_samples;
        fd->sample_rate = metadata->data.stream_info.sample_rate;
        fd->channels = metadata->data.stream_info.channels;
        fd->bps = metadata->data.stream_info.bits_per_sample;

        LOG_INFO("sample rate    : %u Hz\n", fd->sample_rate);
        LOG_INFO("channels       : %u\n", fd->channels);
        LOG_INFO("bits per sample: %u\n", fd->bps);
        LOG_INFO("total samples  : %I64u\n", fd->total_samples);

        uint64_t memsize = fd->total_samples * fd->channels * sizeof(int16_t);
        fd->wav = malloc(memsize);
        assert(fd->wav != NULL);
        LOG_INFO("malloc size    : %I64u Mib\n", memsize / 1024 / 1024);
    }
}

void error_callback(const FLAC__StreamDecoder *decoder,
                    FLAC__StreamDecoderErrorStatus status, void *client_data) {

    LOG_ERR("Got error callback: %s\n",
            FLAC__StreamDecoderErrorStatusString[status]);
    assert(0);
}

int fd_decode(FlacData * fd, const char *file) {
    assert(fd != NULL);
    memset(fd, 0, sizeof(FlacData));
    FLAC__StreamDecoder *decoder = 0;
    FLAC__StreamDecoderInitStatus init_status;
    int ok = true;

    LOG_INFO("start decode \"%s\"\n", file);
    if ((decoder = FLAC__stream_decoder_new()) == NULL) {
        LOG_INFO("allocating decoder\n");
        return 1;
    }

    init_status = FLAC__stream_decoder_init_file(
        decoder, file, write_callback, metadata_callback, error_callback, fd);
    if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
        LOG_ERR("initializing decoder: %s\n",
                FLAC__StreamDecoderInitStatusString[init_status]);
        ok = false;
    }

    if (ok) {
        ok = FLAC__stream_decoder_process_until_end_of_stream(decoder);
        LOG_INFO("decoding: %s\n", ok ? "succeeded" : "FAILED");
        LOG_INFO("   state: %s\n",
                FLAC__StreamDecoderStateString[FLAC__stream_decoder_get_state(
                    decoder)]);
    }

    FLAC__stream_decoder_delete(decoder);
    return 0;
}
